/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.processor;

import lk.mrt.cse.wihidum.communicator.*;
import lk.mrt.cse.wihidum.receiver.*;
import lk.mrt.cse.wihidum.storage.*;
import lk.mrt.cse.wihidum.util.*;
import org.apache.axiom.om.*;
import org.apache.axiom.soap.*;
import org.apache.log4j.*;

import javax.xml.namespace.*;
import java.util.Iterator;
import java.util.ArrayList;

/**
 * Process publication messages
 */
public class PublicationProcessor
        implements Processor
{

    private static final Logger logger = Logger.getLogger(PublicationProcessor.class);
    Router router;

    /**
     * @see lk.mrt.cse.wihidum.processor.Processor#processMessage(org.apache.axiom.soap.SOAPEnvelope)
     */
    public OMElement processMessage(SOAPEnvelope publicationEnvelope)
    {
        logger.info("Processing publication message");
        logger.info(publicationEnvelope);
        System.out.print(publicationEnvelope);
        try
        {
            PublicationMessage publicationMessage = new PublicationMessage();

            publicationMessage.setAction(WihidumConstants.Operations.PUBLISH);

            SOAPHeader soapHeader = publicationEnvelope.getHeader();
            OMElement publicationHeader = soapHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, WihidumConstants.MessageElements.PUBLICATION_HEADER));

            SOAPBody soapBody = publicationEnvelope.getBody();
            publicationMessage.setBody(soapBody.getText());

            // Get response by processing the publicationMessage
            OMElement publicationResponseElement = this.processPublicationHeader(publicationHeader, publicationMessage);

            return publicationResponseElement;
        }
        catch (NullPointerException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
        catch (OMException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
    }

    public OMElement processPropogatingMessage(SOAPEnvelope publicationEnvelope)
    {
        logger.info("Processing broker publication message");
        logger.info(publicationEnvelope);
        try
        {
            PublicationMessage publicationMessage = new PublicationMessage();

            SOAPHeader soapHeader = publicationEnvelope.getHeader();

            OMElement publicationHeaderElement = soapHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, WihidumConstants.MessageElements.PUBLICATION_HEADER));

            SOAPBody publicationEnvelopBody = publicationEnvelope.getBody();
            publicationMessage.setBody(publicationEnvelopBody.getText());

            publicationMessage.setAction(WihidumConstants.Operations.BROKER_PUBLISH);


            //decode broker publication header
            OMElement brokerPublicationHeader = soapHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, WihidumConstants.MessageElements.BROKER_PUBLICATION_HEADER));

            OMElement actionElement = brokerPublicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "Action"));
            publicationMessage.setBrokerPublicationElementAction(actionElement.getText());

            OMElement toElement = brokerPublicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "To"));
            OMElement toAddressElemnt = toElement.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "Address"));
            publicationMessage.setBrokerPublicationElementTo(toAddressElemnt.getText());

            OMElement fromElement = brokerPublicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "From"));
            OMElement fromAddressElemnt = fromElement.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "Address"));
            publicationMessage.setBrokerPublicationElementFrom(fromAddressElemnt.getText());

            OMElement distriutionTraceElement = brokerPublicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "DistributionTrace"));
            OMElement startElement = distriutionTraceElement.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "StartStation"));
            publicationMessage.setBrokerPublicationElementStartStation(startElement.getText());

            Iterator<OMElement> iterator =  distriutionTraceElement.getChildrenWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "IntermediateStation"));
            ArrayList<String> intermediateStations = new ArrayList<String>();
            while(iterator.hasNext())
            {
                OMElement intermediateStation = iterator.next();
                intermediateStations.add(intermediateStation.getText());
            }

            publicationMessage.setBrokerPublicationElementIntermediateStations(intermediateStations);

            // Get response by processing the publicationMessage
            OMElement publicationResponseElement = this.processPublicationHeader(publicationHeaderElement, publicationMessage);

            return publicationResponseElement;
        }
        catch (NullPointerException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
        catch (OMException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
    }

    private OMElement processPublicationHeader(OMElement publicationHeader, PublicationMessage publicationMessage)
    {
        try
       {
        logger.info("Publication header");
        logger.info(publicationHeader);
        // Get publicationMessage event source
        OMElement replyToElement = publicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "ReplyTo"));
        OMElement addressElement = replyToElement.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "Address"));
        String address = addressElement.getText();
        publicationMessage.setEventSourceURL(address);

        // Get publicationMessage topic
        OMElement topicElement = publicationHeader.getFirstChildWithName(new QName(WSEventingConstants.WIHIDUM_NAMESPACE, "Topic"));
        String topic = topicElement.getText();
        publicationMessage.setTopic(topic);

        // Create response to the publicationMessage
        OMElement publicationResponseElement = createPublicationResponseElement(publicationMessage.getEventSourceURL());

        // Save publicationMessage
        this.savePublicationMessage(publicationMessage);

        // Enqueue publicationMessage to be sent to subscribers
        MessageReceiver.getThreadPool().enqueuePublication(publicationMessage);
        return publicationResponseElement;
        }
        catch (NullPointerException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
        catch (OMException ex)
        {
            logger.error("Error in processing received publication", ex);
            return createPublicationFaultResponseElement();
        }
    }

    private void savePublicationMessage(PublicationMessage publicationMessage)
    {
        PublicationManager publicationDAO = new PublicationDAO();
        Publication publication = new Publication();
        publication.setId(publicationMessage.getId());
        publication.setTopic(publicationMessage.getTopic());
        publication.setEventSourceURL(publicationMessage.getEventSourceURL());
        publication.setBody(publicationMessage.getBody());
        publicationDAO.savePublication(publication);
    }

    /**
     * Create response for a valid publication
     *
     * @param sourceEPR Address of the publication event source
     * @return Response element
     */
    private OMElement createPublicationResponseElement(String sourceEPR)
    {
        SOAPFactory factory = OMAbstractFactory.getSOAP11Factory();
        OMNamespace wihidumNS = factory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE, WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);

        OMElement responseElement = factory.createOMElement("PublishResponse", wihidumNS);
        OMElement referenceParameters = factory.createOMElement("ReferenceParameters", wihidumNS);
        OMElement sourceEPRElement = factory.createOMElement("SourceEPR", wihidumNS);

        sourceEPRElement.setText(sourceEPR);
        referenceParameters.addChild(sourceEPRElement);
        responseElement.addChild(referenceParameters);

        return responseElement;
    }

    //TODO: Implement Error Channel
    /**
     * Create response for a invalid publication
     *
     * @return Response element
     */
    private OMElement createPublicationFaultResponseElement()
    {
        SOAPFactory factory = OMAbstractFactory.getSOAP11Factory();
        OMNamespace wihidumNS = factory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE, WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);

        OMElement responseElement = factory.createOMElement("WihidumFault", wihidumNS);

        responseElement.setText("Error in processing publication");
        return responseElement;
    }
}
